/*
 * @Author: mikey.zhaopeng
 * @Date: 2020-05-19 10:01:03
 * @Last Modified by: czy
 * @Last Modified time: 2020-05-19 10:47:12
 */
package com.ht.managermentserver.aop;

import com.ht.managermentserver.utils.JSONUtil;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/** @author czy */
@Aspect
@Slf4j
@Component
public class LogAspect {

  /**
   * @Description 前置通知, 在方法执行之前执行 通用参数
   *
   * <p>{@code @Before} 前置通知, 在方法执行之前执行
   *
   * <p>{@code @After} 后置通知, 在方法执行之后执行 。
   *
   * <p>{@code @AfterRunning} 返回通知, 在方法返回结果之后执行
   *
   * <p>{@code @AfterThrowing} 异常通知, 在方法抛出异常之后
   *
   * <p>{@code @Around} 环绕通知, 围绕着方法执行 参考链接 {@link
   * https://blog.csdn.net/u010502101/article/details/78823056}
   */
  @Before("logAspectController()")
  public static void logRequestInfo(final JoinPoint joinpoint) throws Exception {
    final ServletRequestAttributes requestAttributes =
        (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();

    final HttpServletRequest request = requestAttributes.getRequest();

    final StringBuilder builder = new StringBuilder();

    final MethodSignature signature = (MethodSignature) joinpoint.getSignature();

    final Method method = signature.getMethod();
    final ApiOperation anntion = method.getAnnotation(ApiOperation.class);
    final String annt =
        anntion != null ? anntion.value() : method.getClass().getName() + "." + method.getName();
    builder.append("\r\n\r\n***************** START *********************").append("\r\n");
    builder.append("*********** Swagger: ").append(annt).append("\r\n");
    builder.append("*********** 请求信息: ").append("url = ");
    builder.append(request.getRequestURI()).append(",\r\n");
    builder
        .append("*********** 请求方式: ")
        .append("method = ")
        .append(request.getMethod())
        .append(",\r\n");
    builder
        .append("*********** 请求IP: ")
        .append("IP = ")
        .append(request.getRemoteAddr())
        .append(",\r\n");
    builder
        .append("*********** 类方法: ")
        .append(signature.getDeclaringTypeName())
        .append(",\t")
        .append(signature.getName())
        .append(",\r\n");

    final String[] paramNames = signature.getParameterNames();
    final Object[] paramValues = joinpoint.getArgs();
    final int paramLength = null == paramNames ? 0 : paramNames.length;
    if (paramLength == 0) {
      builder.append("*********** 请求参数 = {} ");
    } else {
      builder.append("请求参数 = [");
      for (int i = 0; i < paramLength - 1; i++) {
        builder
            .append(paramNames[i])
            .append("=")
            .append(JSONUtil.toJsonString(paramValues[i]))
            .append(",");
      }
      builder
          .append(paramNames[paramLength - 1])
          .append("=")
          .append(JSONUtil.toJsonString(paramValues[paramLength - 1]))
          .append("]");
    }
    builder.append("\r\n***************** END *********************").append("\r\n");
    log.info(builder.toString());
  }

  /**
   * 方法执行后
   *
   * @param resultVO
   * @throws Exception
   */
  @AfterReturning(returning = "result", pointcut = "logAspectController()")
  public static void logResultInfo(final Object result) throws Exception {
    log.info("请求结果：" + JSONUtil.toJsonString(result) + "\t");
  }

  /**
   * 定义 切入点 方便通用也可以直接 @Before(execution(**))切面到controller @Description
   * 格式:execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?
   * name-pattern(param-pattern)throws-pattern?)
   *
   * <ul>
   *   <li>修饰符匹配（modifier-pattern?）
   *   <li>返回值匹配（ret-type-pattern）可以为*表示任何返回值,全路径的类名等
   *   <li>类路径匹配（declaring-type-pattern?）
   *   <li>方法名匹配（name-pattern）可以指定方法名 或者 *代表所有, set* 代表以set开头的所有方法
   *   <li>参数匹配（(param-pattern)）可以指定具体的参数类型，多个参数间用“,”隔开，各个参数也可以用“*”来表示匹配任意类型的参数，如(String)表示匹配一个String参数的方法；(*,String)
   *       表示匹配有两个参数的方法，第一个参数可以是任意类型，而第二个参数是String类型；可以用(..)表示零个或多个任意参数
   *   <li>异常类型匹配（throws-pattern?）
   *   <li>其中后面跟着“?”的是可选项
   * </ul>
   *
   * @说明 - 修饰符匹配（modifier-pattern?）; - 返回值匹配（ret-type-pattern）可以为*表示任何返回值,全路径的类名等
   *
   * @author czy @CreateDate 2020-05-19 10:45:22
   * @see {@link https://blog.csdn.net/corbin_zhang/article/details/80576809} {@link
   *     https://www.cnblogs.com/liaojie970/p/7883687.html}
   */
  @Pointcut("execution(public * com.ht.managermentserver.controller..*.*(..))")
  public void logAspectController() {}
}
